package io.renren.common.utils;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.apache.commons.io.IOUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

/**
 * mongodb curd 工具类
 *
 * @author 6346
 */
@Component
public class MongodbUtils {

    public static MongodbUtils mongodbUtils;

    @PostConstruct
    public void init() {
        mongodbUtils = this;
        mongodbUtils.mongoTemplate = this.mongoTemplate;
        mongodbUtils.gridFsTemplate = this.gridFsTemplate;
        mongodbUtils.mongoDbFactory = this.mongoDbFactory;
        mongodbUtils.gridFSBucket = this.gridFSBucket;
    }

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Resource
    private MongoDbFactory mongoDbFactory;
    @Resource
    private GridFSBucket gridFSBucket;

    @Bean
    public GridFSBucket getGridFSBuckets() {
        MongoDatabase db = mongoDbFactory.getDb();
        return GridFSBuckets.create(db);
    }

    /**
     * 保存数据对象，集合为数据对象中@Document 注解所配置的collection
     *
     * @param obj 数据对象
     */
    public static void save(Object obj) {
        mongodbUtils.mongoTemplate.save(obj);
    }

    /**
     * 指定集合保存数据对象
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     */
    public static void save(Object obj, String collectionName) {
        mongodbUtils.mongoTemplate.save(obj, collectionName);
    }

    /**
     * 根据数据对象中的id删除数据，集合为数据对象中@Document 注解所配置的collection
     *
     * @param obj 数据对象
     */
    public static void remove(Object obj) {

        mongodbUtils.mongoTemplate.remove(obj);
    }

    /**
     * 指定集合 根据数据对象中的id删除数据
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     */
    public static void remove(Object obj, String collectionName) {

        mongodbUtils.mongoTemplate.remove(obj, collectionName);
    }

    /**
     * 根据key，value到指定集合删除数据
     *
     * @param key            键
     * @param value          值
     * @param collectionName 集合名
     */
    public static void removeById(String key, Object value, String collectionName) {

        Criteria criteria = Criteria.where(key).is(value);
        criteria.and(key).is(value);
        Query query = Query.query(criteria);
        mongodbUtils.mongoTemplate.remove(query, collectionName);
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public static void updateFirst(String accordingKey, Object accordingValue, String[] updateKeys, Object[] updateValues,
                                   String collectionName) {

        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        mongodbUtils.mongoTemplate.updateFirst(query, update, collectionName);
    }

    /**
     * 指定集合 修改数据，且修改所找到的所有数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public static void updateMulti(String accordingKey, Object accordingValue, String[] updateKeys, Object[] updateValues,
                                   String collectionName) {

        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        mongodbUtils.mongoTemplate.updateMulti(query, update, collectionName);
    }

    /**
     * 根据条件查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
     *
     * @param obj        数据对象
     * @param findKeys   查询条件 key
     * @param findValues 查询条件 value
     * @return
     */
    public static List<? extends Object> find(Object obj, String[] findKeys, Object[] findValues) {

        Criteria criteria = null;
        for (int i = 0; i < findKeys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeys[i]).is(findValues[i]);
            } else {
                criteria.and(findKeys[i]).is(findValues[i]);
            }
        }
        Query query = Query.query(criteria);
        List<? extends Object> resultList = mongodbUtils.mongoTemplate.find(query, obj.getClass());
        return resultList;
    }

    /**
     * 指定集合 根据条件查询出所有结果集
     *
     * @param obj            数据对象
     * @param findKeys       查询条件 key
     * @param findValues     查询条件 value
     * @param collectionName 集合名
     * @return
     */
    public static List<? extends Object> find(Object obj, String[] findKeys, Object[] findValues, String collectionName) {

        Criteria criteria = null;
        for (int i = 0; i < findKeys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeys[i]).is(findValues[i]);
            } else {
                criteria.and(findKeys[i]).is(findValues[i]);
            }
        }
        Query query = Query.query(criteria);
        List<? extends Object> resultList = mongodbUtils.mongoTemplate.find(query, obj.getClass(), collectionName);
        return resultList;
    }

    /**
     * 指定集合 根据条件查询出所有结果集 并排倒序
     *
     * @param obj            数据对象
     * @param findKeys       查询条件 key
     * @param findValues     查询条件 value
     * @param collectionName 集合名
     * @param sort           排序字段
     * @return
     */
    public static List<? extends Object> find(Object obj, String[] findKeys, Object[] findValues, String collectionName, String sort) {

        Criteria criteria = null;
        for (int i = 0; i < findKeys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeys[i]).is(findValues[i]);
            } else {
                criteria.and(findKeys[i]).is(findValues[i]);
            }
        }
        Query query = Query.query(criteria);
        query.with(new Sort(Direction.DESC, sort));
        List<? extends Object> resultList = mongodbUtils.mongoTemplate.find(query, obj.getClass(), collectionName);
        return resultList;
    }

    /**
     * 根据条件查询出符合的第一条数据 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param obj        数据对象
     * @param findKeys   查询条件 key
     * @param findValues 查询条件 value
     * @return
     */
    public static Object findOne(Object obj, String[] findKeys, Object[] findValues) {

        Criteria criteria = null;
        for (int i = 0; i < findKeys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeys[i]).is(findValues[i]);
            } else {
                criteria.and(findKeys[i]).is(findValues[i]);
            }
        }
        Query query = Query.query(criteria);
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj.getClass());
        return resultObj;
    }

    /**
     * 指定集合 根据条件查询出符合的第一条数据
     *
     * @param obj            数据对象
     * @param findKeys       查询条件 key
     * @param findValues     查询条件 value
     * @param collectionName 集合名
     * @return
     */
    public static Object findOne(Object obj, String[] findKeys, Object[] findValues, String collectionName) {

        Criteria criteria = null;
        for (int i = 0; i < findKeys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeys[i]).is(findValues[i]);
            } else {
                criteria.and(findKeys[i]).is(findValues[i]);
            }
        }
        Query query = Query.query(criteria);
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj.getClass(), collectionName);
        return resultObj;
    }

    /**
     * 查询出所有结果集 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param obj 数据对象
     * @return
     */
    public static List<? extends Object> findAll(Object obj) {

        List<? extends Object> resultList = mongodbUtils.mongoTemplate.findAll(obj.getClass());
        return resultList;
    }

    /**
     * 指定集合 查询出所有结果集
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     * @return
     */
    public static List<? extends Object> findAll(Object obj, String collectionName) {
        List<? extends Object> resultList = mongodbUtils.mongoTemplate.findAll(obj.getClass(), collectionName);
        return resultList;
    }

    /**
     * 查询文件信息
     */
    public static GridFSFile getFileInfo(String objectId) {
        Query query = Query.query(Criteria.where("_id").is(objectId));
        // 查询单个文件
        return mongodbUtils.gridFsTemplate.findOne(query);
    }

    /**
     * 存储文件
     * metadata.put("test","test"); //保存额外的信息
     */
    public static GridFSFile saveFile(MultipartFile request, Document metadata) throws Exception {
        // 获得提交的文件名
        String fileName = request.getOriginalFilename();
        // 获得提交的文件后缀名
        //String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
        // 获得文件输入流
        InputStream inputStream = request.getInputStream();
        // 获得文件类型
        String contentType = request.getContentType();
        //保存进数据库
        ObjectId objId = mongodbUtils.gridFsTemplate.store(inputStream, fileName, contentType, metadata);
        return mongodbUtils.getFileInfo(objId + "");
    }

    /**
     * 下载文件
     */
    public static void downFile(HttpServletRequest request, HttpServletResponse response, String objectId) throws Exception {
        Query query = Query.query(Criteria.where("_id").is(objectId));
        // 查询单个文件
        GridFSFile gfsFile = mongodbUtils.gridFsTemplate.findOne(query);
        GridFSDownloadStream gridFSDownloadStream = mongodbUtils.gridFSBucket.openDownloadStream(gfsFile.getObjectId());
        GridFsResource gridFsResource = new GridFsResource(gfsFile, gridFSDownloadStream);
        String fileName = gfsFile.getFilename().replace(",", "");
        if (gfsFile == null) {
            return;
        }
        //处理中文文件名乱码
        response.setHeader("Content-Disposition", "attachment;filename="
                .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
        IOUtils.copy(gridFsResource.getInputStream(), response.getOutputStream());
    }

    /**
     * 删除文件
     */
    public static boolean deleteFile(String objectId) {
        Query query = Query.query(Criteria.where("_id").is(objectId));
        // 查询单个文件
        GridFSFile gfsFile = mongodbUtils.gridFsTemplate.findOne(query);
        if (gfsFile == null) {
            return false;
        }
        mongodbUtils.gridFsTemplate.delete(query);
        return true;
    }

    /**
     * 展示图片
     */
    public static void showImage(HttpServletResponse response, String objectId) throws Exception {
        Query query = Query.query(Criteria.where("_id").is(objectId));
        // 查询单个文件
        GridFSFile gfsFile = mongodbUtils.gridFsTemplate.findOne(query);
        if (gfsFile == null) {
            return;
        }
        GridFSDownloadStream gridFSDownloadStream = mongodbUtils.gridFSBucket.openDownloadStream(gfsFile.getObjectId());
        GridFsResource gridFsResource = new GridFsResource(gfsFile, gridFSDownloadStream);
        String type = gridFsResource.getContentType();
        response.setContentType(type);
        response.setHeader("Cache-Control", "no-store, no-cache");
        BufferedImage bi = ImageIO.read(gridFsResource.getInputStream());
        ServletOutputStream to = response.getOutputStream();
        ImageIO.write(bi, gfsFile.getMetadata().get("suffix") + "", to);
    }

    /**
     * 获取图片BASE64
     *
     * @param objectId
     * @return InputStream
     * @throws Exception
     */
    public static String getImageInputStream(String objectId) throws IOException {
        Query query = Query.query(Criteria.where("_id").is(objectId));
        GridFSFile gfsFile = mongodbUtils.gridFsTemplate.findOne(query);
        GridFSDownloadStream gridFSDownloadStream = mongodbUtils.gridFSBucket.openDownloadStream(gfsFile.getObjectId());
        GridFsResource gridFsResource = new GridFsResource(gfsFile, gridFSDownloadStream);
        byte[] data = IOUtils.toByteArray(gridFsResource.getInputStream());
        BASE64Encoder encoder = new BASE64Encoder();
        return data != null ? encoder.encode(data) : "";
    }

    /**
     * 批量获取图片BASE64
     *
     * @param imageMap
     * @return InputStream
     * @throws Exception
     */
    public static Map<String, String> getImageInputStreamBatch(Map<String, String> imageMap) throws IOException {
        for (String key : imageMap.keySet()) {
            imageMap.put(key, getImageInputStream(imageMap.get(key)));
        }
        return imageMap;
    }
}
